package org.dandelero.properties.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Map.Entry;

import org.dandelero.properties.framework.Predicate;

/**
 * A set of methods for operating on properties.
 * 
 * @author dandelero
 */
public class PropertyUtils {

	/**
	 * Finds all the keys in the given properties object that match the specified predicate.
	 * 
	 * @param properties
	 *            the properties.
	 * @param predicate
	 *            the predicate to apply.
	 * @return a new properties object with all the matching keys.
	 */
	public static Properties findKeys(Properties properties, Predicate<String> predicate) {
		final Properties result = new Properties();
		if (predicate == null) {
			return result;
		}

		for (Entry<Object, Object> entry : properties.entrySet()) {
			if (predicate.evaluate((String) entry.getKey())) {
				result.put(entry.getKey(), entry.getValue());
			}
		}
		return result;
	}

	/**
	 * Filters items in the given properties object by applying the specified predicate to each key, keeping only the
	 * keys that the predicate evaluates false for.
	 * 
	 * @param properties
	 *            the properties reference object to be filtered.
	 * @param predicate
	 *            the predicate to apply for filtering.
	 */
	public static void filterKeys(Properties properties, Predicate<String> predicate) {
		if (predicate == null) {
			return;
		}

		synchronized (properties) {
			// Find all the matching properties first.
			final Properties matchingProperties = findKeys(properties, predicate);

			// Now remove the matching properties from the reference.
			for (Entry<Object, Object> entry : matchingProperties.entrySet()) {
				properties.remove(entry.getKey());
			}
		}
	}

	/**
	 * Filters items in the given properties object by applying the specified predicate to each key, keeping only the
	 * keys that the predicate evaluates successfully for.
	 * 
	 * @param properties
	 *            the properties reference object to be filtered.
	 * @param predicate
	 *            the predicate to apply for filtering.
	 */
	public static void retainKeys(Properties properties, Predicate<String> predicate) {
		if (predicate == null) {
			return;
		}

		synchronized (properties) {
			// Find all the matching properties first.
			final Properties matchingProperties = findKeys(properties, predicate);

			final List<Object> keysToBeRemoved = new ArrayList<Object>();
			// Now remove the non-matching properties from the reference.
			for (Entry<Object, Object> entry : properties.entrySet()) {
				if (!matchingProperties.containsKey(entry.getKey())) {
					keysToBeRemoved.add(entry.getKey());
				}
			}
			for (Object key : keysToBeRemoved) {
				properties.remove(key);
			}
		}
	}
}
